package cn.dvptech.remotemate.other

import android.graphics.SurfaceTexture
import android.media.MediaCodec
import android.media.MediaCodec.INFO_OUTPUT_FORMAT_CHANGED
import android.media.MediaCodecInfo
import android.media.MediaFormat
import android.opengl.EGL14
import android.opengl.EGLConfig
import android.opengl.EGLContext
import android.opengl.EGLDisplay
import android.opengl.EGLSurface
import android.opengl.GLES11Ext
import android.opengl.GLES20
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.view.Surface
import androidx.annotation.RequiresApi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.nio.ByteBuffer
import java.nio.ByteOrder

/**
 *    author  : ulongx
 *    website : www.dvptech.cn
 *    date    : 2024/7/9
 *    desc    :
 */

class VideoDecoderGL(private val surface: Surface) {

    private var mediaCodec: MediaCodec? = null
    private var isDecoding = false
    private var frameCount = 0L
    private val decodeScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
    private val frameDataQueue = Channel<Pair<ByteBuffer, Long>>(Channel.UNLIMITED)
    private val frameRate = 30

    private lateinit var surfaceTexture: SurfaceTexture
    private lateinit var eglContext: EGLContext
    private lateinit var eglDisplay: EGLDisplay
    private lateinit var eglSurface: EGLSurface
    private val renderThread = HandlerThread("VideoRenderThread").apply { start() }
    private val renderHandler = Handler(renderThread.looper)

    // OpenGL ES 相关变量
    private var program: Int = 0
    private var positionHandle: Int = 0
    private var texCoordHandle: Int = 0
    private var textureHandle: Int = 0
    private val vertexBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE_BYTES * 8).order(ByteOrder.nativeOrder()).asFloatBuffer()
    private val texCoordBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE_BYTES * 8).order(ByteOrder.nativeOrder()).asFloatBuffer()

    @RequiresApi(Build.VERSION_CODES.O)
    fun startDecoding(width: Int, height: Int) {
        if (isDecoding) return

        val videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC, width, height).apply {
            setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
            setInteger(MediaFormat.KEY_BIT_RATE, width * height)
            setInteger(MediaFormat.KEY_FRAME_RATE, frameRate)
            setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1)
        }

        mediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC)
        surfaceTexture = SurfaceTexture(0).apply {
            setDefaultBufferSize(width, height)
            setOnFrameAvailableListener {
                renderHandler.post { drawFrame() }
            }
        }
        val decoderSurface = Surface(surfaceTexture)

        mediaCodec?.configure(videoFormat, decoderSurface, null, 0)
        mediaCodec?.start()

        isDecoding = true
        decodeScope.launch { decodeLoop() }
        // 在渲染线程中初始化 EGL
        renderHandler.post {
            initEGL(surface)
            initGL()
        }
    }

    private suspend fun decodeLoop() = withContext(Dispatchers.IO) {
        val bufferInfo = MediaCodec.BufferInfo()
        while (isDecoding) {
            try {
                val inputBufferIndex = mediaCodec?.dequeueInputBuffer(1000) ?: -1
                if (inputBufferIndex >= 0) {
                    val inputBuffer = mediaCodec?.getInputBuffer(inputBufferIndex)
                    val (frameData, _) = frameDataQueue.receive()
                    inputBuffer?.clear()
                    inputBuffer?.put(frameData)
                    val calculatedPresentationTimeUs = (frameCount * 1_000_000_000L / frameRate).toLong()
                    mediaCodec?.queueInputBuffer(
                        inputBufferIndex,
                        0,
                        frameData.limit(),
                        calculatedPresentationTimeUs,
                        0
                    )
                    frameCount++
                }

                val outputBufferIndex = mediaCodec?.dequeueOutputBuffer(bufferInfo, 1000) ?: -1
                if (outputBufferIndex >= 0) {
                    mediaCodec?.releaseOutputBuffer(outputBufferIndex, true)
                } else if (outputBufferIndex == INFO_OUTPUT_FORMAT_CHANGED) {
                    // Handle output format changes if needed
                }
            } catch (e: Exception) {
                Timber.e("Error in decodeLoop: $e")
                // 处理错误，例如停止解码或通知用户
            }
        }
    }

    fun decodeFrame(frameData: ByteBuffer) {
        if (!isDecoding) return
        decodeScope.launch {
            frameDataQueue.send(Pair(frameData, 0L))
        }
    }

    fun stopDecoding() {
        isDecoding = false
        decodeScope.cancel()

        // 在渲染线程上同步释放资源
        renderHandler.post {
            mediaCodec?.stop()
            mediaCodec?.release()
            mediaCodec = null
            surfaceTexture.release()
            releaseEGL()
        }

        renderHandler.removeCallbacksAndMessages(null)
        renderThread.quitSafely()
        frameCount = 0
    }

    private fun initEGL(surface: Surface) {
        eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
        if (eglDisplay == EGL14.EGL_NO_DISPLAY) {
            Timber.e( "Failed to get EGL display")
            return // 或抛出异常
        }
        val version = IntArray(2)
        EGL14.eglInitialize(eglDisplay, version, 0, version, 1)

        val configAttr = intArrayOf(
            EGL14.EGL_RED_SIZE, 8,
            EGL14.EGL_GREEN_SIZE, 8,
            EGL14.EGL_BLUE_SIZE, 8,
            EGL14.EGL_ALPHA_SIZE, 8,
            EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
            EGL14.EGL_NONE
        )
        val configs = arrayOfNulls<EGLConfig>(1)
        val numConfigs = IntArray(1)
        EGL14.eglChooseConfig(eglDisplay, configAttr, 0, configs, 0, configs.size, numConfigs, 0)
        val eglConfig = configs[0]!!

        val contextAttr = intArrayOf(EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE)
        eglContext = EGL14.eglCreateContext(eglDisplay, eglConfig, EGL14.EGL_NO_CONTEXT, contextAttr, 0)

        val surfaceAttr = intArrayOf(EGL14.EGL_NONE)
        eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, surface, surfaceAttr, 0)

        if (eglSurface == EGL14.EGL_NO_SURFACE) {
            Timber.e( "Failed to create EGL window surface")
            return // 或抛出异常
        }

        if (!EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
            Timber.e( "Failed to make EGL context current")
            return // 或抛出异常
        }
    }

    private fun releaseEGL() {
        EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)
        EGL14.eglDestroySurface(eglDisplay, eglSurface)
        EGL14.eglDestroyContext(eglDisplay, eglContext)
        EGL14.eglTerminate(eglDisplay)
    }

    // OpenGL ES 相关函数
    private val vertexShaderCode =
        """
        attribute vec4 vPosition;
        attribute vec2 texCoord;
        varying vec2 vTexCoord;
        void main() {
            gl_Position = vPosition;
            vTexCoord = texCoord;
        }
    """.trimIndent()

    private val fragmentShaderCode =
        """
        #extension GL_OES_EGL_image_external : require
        precision mediump float;
        varying vec2 vTexCoord;
        uniform samplerExternalOES sTexture;
        void main() {
            gl_FragColor = texture2D(sTexture, vTexCoord);
        }
    """.trimIndent()

    private fun initGL() {
        // 编译着色器
        val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)

        // 创建 OpenGL ES 程序
        program = GLES20.glCreateProgram()
        GLES20.glAttachShader(program, vertexShader)
        GLES20.glAttachShader(program, fragmentShader)
        GLES20.glLinkProgram(program)

        // 获取属性和统一变量的位置
        positionHandle = GLES20.glGetAttribLocation(program, "vPosition")
        texCoordHandle = GLES20.glGetAttribLocation(program, "texCoord")
        textureHandle = GLES20.glGetUniformLocation(program, "sTexture")

        // 设置顶点坐标
        vertexBuffer.put(floatArrayOf(-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f))
        vertexBuffer.position(0)

        // 设置纹理坐标
        texCoordBuffer.put(floatArrayOf(0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f))
        texCoordBuffer.position(0)
    }

    private fun loadShader(type: Int, shaderCode: String): Int {
        val shader = GLES20.glCreateShader(type)
        GLES20.glShaderSource(shader, shaderCode)
        GLES20.glCompileShader(shader)
        return shader
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun drawFrame() {

        if (surfaceTexture.isReleased) {
            return // 如果 SurfaceTexture 已被释放，则不进行渲染
        }

        // 更新 SurfaceTexture
        surfaceTexture.updateTexImage()

        // 清除屏幕
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)

        // 使用 OpenGL ES 程序
        GLES20.glUseProgram(program)

        // 启用顶点属性
        GLES20.glEnableVertexAttribArray(positionHandle)
        GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer)

        // 启用纹理坐标属性
        GLES20.glEnableVertexAttribArray(texCoordHandle)
        GLES20.glVertexAttribPointer(texCoordHandle, 2, GLES20.GL_FLOAT, false, 0, texCoordBuffer)

        // 绑定纹理
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0) // 假设纹理 ID 为 0
        GLES20.glUniform1i(textureHandle, 0)

        // 绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)

        // 禁用顶点属性
        GLES20.glDisableVertexAttribArray(positionHandle)
        GLES20.glDisableVertexAttribArray(texCoordHandle)

        // 交换缓冲区
        EGL14.eglSwapBuffers(eglDisplay, eglSurface)
    }

    companion object {
        private const val FLOAT_SIZE_BYTES = 4
    }
}